package org.rsbot.script;

import java.awt.Point;
import org.rsbot.bot.Bot;
import org.rsbot.script.util.Filter;
import org.rsbot.script.wrappers.RSInterface;
import org.rsbot.script.wrappers.RSInterfaceComponent;
import org.rsbot.script.wrappers.RSItem;
import org.rsbot.script.wrappers.RSNPC;
import org.rsbot.script.wrappers.RSObject;
import org.rsbot.script.wrappers.RSTile;

/**
 * This class is for all the Bank operations.
 */
public class Bank {

    private final Methods methods;
    public static final int[] Bankers = {44, 45, 494, 495, 499, 553, 958,
        1036, 2271, 2354, 2355, 2759, 3824, 5488, 5901, 5912, 5913, 6362,
        6532, 6533, 6534, 6535, 7605, 8948, 9710, 14367};
    public static final int[] BankBooths = {782, 2213, 6084, 11402, 11758,
        12759, 14367, 19230, 22819, 24914, 25808, 26972, 27663, 29085,
        34752, 35647, 36786};
    public static final int[] BankChests = {4483, 12308, 21301, 27663, 42192};
    public static final int[] BankDepositBox = {9398, 20228, 26969, 36788};
    public static final RSTile[] UnreachableBankers = {
        new RSTile(3191, 3445), new RSTile(3180, 3433)}; // VARROCK EAST
    public static final int INTERFACE_BANK = 762;
    public static final int INTERFACE_BANK_BUTTON_CLOSE = 43;
    public static final int INTERFACE_BANK_BUTTON_DEPOSIT_BEAST_INVENTORY = 38;
    public static final int INTERFACE_BANK_BUTTON_DEPOSIT_CARRIED_ITEMS = 34;
    public static final int INTERFACE_BANK_BUTTON_DEPOSIT_WORN_ITEMS = 36;
    public static final int INTERFACE_BANK_BUTTON_HELP = 44;
    public static final int INTERFACE_BANK_BUTTON_INSERT = 15;
    public static final int INTERFACE_BANK_BUTTON_ITEM = 19;
    public static final int INTERFACE_BANK_BUTTON_NOTE = 19;
    public static final int INTERFACE_BANK_BUTTON_SEARCH = 17;
    public static final int INTERFACE_BANK_BUTTON_SWAP = 15;
    public static final int INTERFACE_BANK_BUTTON_OPEN_EQUIP = 117;
    public static final int INTERFACE_BANK_INVENTORY = 93;
    public static final int INTERFACE_BANK_ITEM_FREE_COUNT = 29;
    public static final int INTERFACE_BANK_ITEM_FREE_MAX = 30;
    public static final int INTERFACE_BANK_ITEM_MEMBERS_COUNT = 31;
    public static final int INTERFACE_BANK_ITEM_MEMBERS_MAX = 32;
    public static final int INTERFACE_BANK_SCROLLBAR = 114;
    public static final int INTERFACE_BANK_SEARCH = 752;
    public static final int INTERFACE_BANK_SEARCH_INPUT = 5;
    public static final int INTERFACE_EQUIPMENT = 667;
    public static final int INTERFACE_EQUIPMENT_COMPONENT = 7;
    public static final int INTERFACE_COLLECTION_BOX = 105;
    public static final int INTERFACE_COLLECTION_BOX_CLOSE = 13;
    public static final int[] INTERFACE_BANK_TAB = {
        63, 61, 59, 57, 55, 53, 51, 49, 47};
    public static final int[] INTERFACE_BANK_TAB_FIRST_ITEM = {
        78, 79, 80, 81, 82, 83, 84, 85, 86};
    public static final int INTERFACE_DEPOSIT_BOX = 11;
    public static final int INTERFACE_DEPOSIT_BOX_BUTTON_CLOSE = 15;
    public static final int INTERFACE_DEPOSIT_BUTTON_DEPOSIT_BEAST_INVENTORY = 22;
    public static final int INTERFACE_DEPOSIT_BUTTON_DEPOSIT_CARRIED_ITEMS = 18;
    public static final int INTERFACE_DEPOSIT_BUTTON_DEPOSIT_WORN_ITEMS = 20;
    public static final int SETTING_BANK_TOGGLE_REARRANGE_MODE = 304;
    public static final int SETTING_BANK_TOGGLE_WITHDRAW_MODE = 115;

    public Bank(final Bot bot) {
        this.methods = bot.methods;
    }

    /**
     * Given a bank booth object, uses the object model
     * to perform the specified option.
     * 
     * @param booth     Bank booth object
     * @param option    If null or "", left clicks the booth, otherwise the
     *                  specified option is performed.
     * @return          <tt>true</tt> if successful, <tt>false</tt> otherwise
     */
    public boolean atBankBooth(final RSObject booth, final String option) {
        boolean rslt;

        if (booth == null || !methods.tileOnScreen(booth.getLocation()))
            return false;

        int ct = 0;
        while (ct++ < 10 && methods.isMenuOpen()) {
            methods.moveMouseRandomly(150);
        }

        if (methods.isMenuOpen())
            return false;

        if (option == null || option == "") {
            rslt = booth.action("Use-quickly");
        } else {
            rslt = booth.action(option);
        }

        return rslt;
    }

    public boolean atBankBooth(final RSTile boothtile, final String option) {
        if (!methods.tileOnScreen(boothtile))
            return false;

        final RSObject booth = methods.getTopObjectAt(boothtile);

        return atBankBooth(booth, option);
    }

    /**
     * Performs a given action on the specified item ID.
     * 
     * @param itemID    The ID of the item.
     * @param txt       The action to perform (see {@link Methods#atMenu}).
     * @return          <tt>true</tt> if successful; otherwise <tt>false</tt>.
     */
    public boolean atItem(final int itemID, final String txt) {
        if (!methods.isLoggedIn() || !isOpen())
            return false;
        final RSInterfaceComponent item = getItemByID(itemID);
        return (item != null) && item.isValid()
                && methods.atInterface(item, txt);

    }

    /**
     * Closes the bank interface.
     *
     * @return      <tt>true</tt> if the bank interface is no longer open.
     */
    public boolean close() {
        if (!isOpen())
            return true;
        if (isOpen()) {
            methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_CLOSE);
            methods.wait(methods.random(500, 600));
            return !isOpen();
        }
        if (isDepositOpen()) {
            methods.atInterface(INTERFACE_DEPOSIT_BOX,
                    INTERFACE_DEPOSIT_BOX_BUTTON_CLOSE);
            methods.wait(methods.random(500, 600));
            return !isDepositOpen();
        }
        return !isOpen();
    }

    /**
     * If bank is open, deposits specified amount of an item into the bank.
     * Supports deposit boxes.
     *
     * @param itemID        The ID of the item.
     * @param number        The amount to deposit. 0 deposits All. 1,5,10 deposit
     *                      corresponding amount while other numbers deposit X.
     * @return              <tt>true</tt> if successful; otherwise <tt>false</tt>.
     */
    public boolean deposit(final int itemID, final int number) {
        if (number < 0)
            throw new IllegalArgumentException("numberToDepsoit < 0 ("
                    + number + ")");
        RSInterfaceComponent item = null;
        int itemCount = 0;
        final int inventoryCount = isOpen() ? methods.getInventoryCount(true)
                : getBoxCount();
        if (!isOpen()) {
            boolean match = false;
            for (int i = 0; i < 28; i++) {
                final RSInterfaceComponent comp = methods.getInterface(11).
                        getChild(17).getComponent(i);
                if (comp.getComponentID() == itemID) {
                    itemCount += comp.getComponentStackSize();
                    if (!match) {
                        item = comp;
                        match = true;
                    }
                }
                if (itemCount > 1) {
                    break;
                }
            }
        } else {
            item = methods.getInventoryItem(itemID).getComponent();
            itemCount = methods.getInventoryCount(true, itemID);
        }
        if (item == null)
            return true;

        switch (number) {
            case 0: /* Deposit All */
                methods.atInventoryItem(itemID, "Deposit-All");
                break;
            case 1: /* Deposit 1 */
                methods.atInventoryItem(itemID, "Deposit");
                break;
            case 5: /* Deposit 5 */
                methods.atInventoryItem(itemID, "Deposit-" + number);
                break;
            default: /* Deposit x */
                if (!methods.atInventoryItem(itemID, "Deposit-" + number)) {
                    if (methods.atInventoryItem(itemID, "Deposit-X")) {
                        methods.wait(100);
                        if (methods.isTextInputOpen()) {
                            methods.wait(methods.random(300, 800));
                            methods.getBot().getInputManager().sendKeys("" + number, true);
                        }
                    }
                    break;
                }
                final int cInvCount = isOpen() ? methods.getInventoryCount(true)
                        : getBoxCount();
                return cInvCount < inventoryCount || cInvCount == 0;
        }
        return false;
    }

    /**
     * Deposits all items in inventory.
     * 
     * @return      <tt>true</tt> on success.
     */
    public boolean depositAll() {
        if (isOpen())
            return methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_DEPOSIT_CARRIED_ITEMS);
        return isDepositOpen() && methods.atInterface(INTERFACE_DEPOSIT_BOX,
                INTERFACE_DEPOSIT_BUTTON_DEPOSIT_CARRIED_ITEMS);
    }

    /**
     * {@link deposit, item}
     * 
     * Deposits based on item stack amount. This is much more human in action
     * that standard bank.depositAll
     */
    public boolean depositAllItem(final int item) {

        if (methods.getInventoryCount(item) > 0) {
            switch (methods.getInventoryCount(item)) {
                case 1:
                    methods.atInventoryItem(item, "Deposit");
                    break;
                case 5:
                    if (methods.random(1, 3) > 1) {
                        methods.atInventoryItem(item, "Deposit-5");
                        break;
                    }
                case 10:
                    if (methods.random(1, 3) > 1) {
                        methods.atInventoryItem(item, "Deposit-10");
                        break;
                    }
                default:
                    methods.atInventoryItem(item, "Deposit-All");
                    break;
            }
            final long inventoryTimeCheck = System.currentTimeMillis();
            while (methods.getInventoryCount(item) != 0
                    && System.currentTimeMillis() - inventoryTimeCheck < 2000) {
                methods.wait(50);
            }
            if (methods.getInventoryCount(item) == 0)
                return true;
            else {
                depositAllItem(item);
            }
        }

        return false;
    }

    /**
     * Deposit everything your player has equipped. Supports deposit boxes.
     *
     * @return      <tt>true</tt> on success.
     * @since       6 March 2009.
     */
    public boolean depositAllEquipped() {
        if (isOpen())
            return methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_DEPOSIT_WORN_ITEMS);
        return isDepositOpen() && methods.atInterface(INTERFACE_DEPOSIT_BOX,
                INTERFACE_DEPOSIT_BUTTON_DEPOSIT_WORN_ITEMS);
    }

    /**
     * Deposits all items in inventory except for the given IDs. If no such IDs
     * in inventory uses button.
     * 
     * @param items     The items not to deposit.
     * @return          <tt>true</tt> on success.
     */
    public boolean depositAllExcept(final int... items) {
        int inventoryCount = methods.getInventoryCount();
        int[] inventoryArray = methods.getInventoryArray();
        outer:
        for (int off = 0; off < inventoryArray.length; off++) {
            if (inventoryArray[off] == -1) {
                continue;
            }
            for (final int item : items) {
                if (inventoryArray[off] == item) {
                    continue outer;
                }
            }

            for (int tries = 0; tries < 5; tries++) {
                depositAllItem(inventoryArray[off]);
                methods.wait(methods.random(500, 700));
                if (methods.getInventoryCount() < inventoryCount) {
                    break;
                }
            }
            if (methods.getInventoryCount() >= inventoryCount)
                return false;
            inventoryArray = methods.getInventoryArray();
            inventoryCount = methods.getInventoryCount();
        }
        return true;
    }

    /**
     * Deposits everything your familiar is carrying. Supports deposit boxes.
     *
     * @return      <tt>true</tt> on success
     * @since       6 March 2009.
     */
    public boolean depositAllFamiliar() {
        if (isOpen())
            return methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_DEPOSIT_BEAST_INVENTORY);
        return isDepositOpen() && methods.atInterface(INTERFACE_DEPOSIT_BOX,
                INTERFACE_DEPOSIT_BUTTON_DEPOSIT_BEAST_INVENTORY);
    }

    /**
     * Gets the count of all items in your inventory ignoring stack sizes while
     * deposit box is open.
     *
     * @return      The count.
     */
    public int getBoxCount() {
        if (!isDepositOpen())
            return -1;
        int count = 0;
        for (int i = 0; i < 28; i++) {
            if (methods.getInterface(11).getComponent(17).isValid()
                    && methods.getInterface(11).getComponent(
                    17).getComponent(i).getComponentID() != -1) {
                count++;
            }
        }
        return count;
    }

    /**
     * Returns the sum of the count of the given items in the bank.
     *
     * @param       items The array of items.
     * @return      The sum of the stacks of the items.
     */
    public int getCount(final int... items) {
        int itemCount = 0;
        final RSItem[] inventoryArray = getItems();
        for (final RSItem item : inventoryArray) {
            for (final int id : items) {
                if (item.getID() == id) {
                    itemCount += item.getStackSize();
                }
            }
        }
        return itemCount;
    }

    /**
     * @param item      the id of the item to wait for
     * @param count     the amount of item to wait for
     * @param timeout   the maximum time in milli seconds to wait for
     * @return          the count of the item in the bank.
     */
    public int waitForBankCount(final int item, final int count, final int timeout) {
        final long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeout) {
            if (getCount(item) >= count) {
                break;
            }
        }
        return getCount(item);
    }

    /**
     * Get current tab open in the bank.
     * 
     * @return      int of tab (0-8), or -1 if none are selected
     *              (bank is not open).
     */
    public int getCurrentTab() {
        for (int i = 0; i < INTERFACE_BANK_TAB.length; i++) {
            if (methods.ifaces.getInterface(INTERFACE_BANK).
                    getChild(INTERFACE_BANK_TAB[i] - 1).getBackgroundColor() == 1419)
                return i;
        }
        return -1; /* no selected ones. Bank may not be open. */
    }

    /**
     * Gets the bank interface.
     * 
     * @return      The bank <code>RSInterface</code>.
     */
    public RSInterface getInterface() {
        return methods.ifaces.getInterface(INTERFACE_BANK);
    }

    /**
     * Gets the deposit box interface.
     *
     * @return      The deposit box <code>RSInterface</code>.
     */
    public RSInterface getBoxInterface() {
        return methods.ifaces.getInterface(INTERFACE_BANK);
    }

    /**
     * Gets the <code>RSComponent</code> of the given item at the specified index.
     *
     * @param       index The index of the item.
     * @return      <code>RSComponent</code> if item is found at index; otherwise null.
     */
    public RSItem getItemAt(final int index) {
        final RSItem[] items = getItems();
        if (items != null) {
            for (final RSItem item : items) {
                if (item.getComponent().getComponentIndex() == index)
                    return item;
            }
        }

        return null;
    }

    /**
     * Gets the first item with the provided ID in the bank.
     *
     * @param       id ID of the item to get.
     * @return      The component of the item; otherwise null.
     */
    public RSItem getItem(final int id) {
        final RSItem[] items = getItems();
        if (items != null) {
            for (final RSItem item : items) {
                if (item.getID() == id)
                    return item;
            }
        }
        return null;
    }

    public RSInterfaceComponent getItemComponent(final int index) {
        final RSInterfaceComponent[] items = getItemsComponent();
        if (items != null) {
            for (final RSInterfaceComponent item : items) {
                if (item.getComponentIndex() == index)
                    return item;
            }
        }

        return null;
    }

    /**
     * Gets all the items in the bank's inventory.
     *
     * @return      an <code>RSItem</code> array of the bank's inventory interface.
     */
    public RSItem[] getItems() {
        if (getInterface() == null || getInterface().
                getComponent(INTERFACE_BANK_INVENTORY) == null)
            return new RSItem[0];
        final RSInterfaceComponent[] components = getInterface().
                getComponent(INTERFACE_BANK_INVENTORY).getComponents();
        final RSItem[] items = new RSItem[components.length];
        for (int i = 0; i < items.length; ++i) {
            items[i] = new RSItem(components[i], methods);
        }
        return items;
    }

    /**
     * Gets the array of item IDs in the bank.
     * 
     * @return      The item IDs array.
     */
    public int[] getItemArray() {
        final RSInterfaceComponent[] items = getInterface().getChild(
                INTERFACE_BANK_INVENTORY).getComponents();
        if (items != null) {
            final int[] value = new int[items.length];
            for (final RSInterfaceComponent item : items) {
                value[item.getComponentIndex()] = item.getComponentID();
            }
            return value;
        }

        return new int[0];
    }

    /**
     * Makes it easier to get Items in the bank. Written by Fusion89k.
     * 
     * @param id        ID of the item to get.
     * @return          The component of the item.
     */
    public RSInterfaceComponent getItemByID(final int id) {
        final RSInterfaceComponent[] items = getItemsComponent();
        if (items != null) {
            for (final RSInterfaceComponent item : items) {
                if (item.getComponentID() == id)
                    return item;
            }
        }

        return null;
    }

    public String[] getItemNames() {
        final RSInterfaceComponent[] items = getInterface().getChild(
                INTERFACE_BANK_INVENTORY).getComponents();
        if (items != null) {
            final String[] value = new String[items.length];
            for (final RSInterfaceComponent item : items) {
                value[item.getComponentIndex()] = item.getComponentName();
            }
            return value;
        }

        return new String[0];
    }

    /**
     * Gets the point on the screen for a given item.
     * Numbered left to right then top to bottom.
     *
     * @param       slot The index of the item.
     * @return      The point of the item or new Point(-1, -1) if null.
     */
    public Point getItemPoint(final int slot) {
        if (slot < 0)
            throw new IllegalArgumentException("slot < 0 " + slot);
        final RSItem item = getItemAt(slot);
        if (item != null)
            return item.getComponent().getLocation();
        return new Point(-1, -1);
    }

    public RSInterfaceComponent[] getItemsComponent() {
        if ((getInterface() == null)
                || (getInterface().getChild(INTERFACE_BANK_INVENTORY) == null))
            return new RSInterfaceComponent[0];

        return getInterface().getChild(INTERFACE_BANK_INVENTORY).getComponents();
    }

    /**
     * Gets the array of item stack sizes in the bank.
     * 
     * @return      The stack sizes array.
     */
    public int[] getStackSizes() {
        final RSInterfaceComponent[] items = getInterface().getChild(
                INTERFACE_BANK_INVENTORY).getComponents();
        if (items != null) {
            final int[] value = new int[items.length];
            for (final RSInterfaceComponent item : items) {
                value[item.getComponentIndex()] = item.getComponentStackSize();
            }
            return value;
        }

        return new int[0];
    }

    /**
     * @return      <tt>true</tt> if the bank interface is open; otherwise
     *              <tt>false</tt>.
     */
    public boolean isOpen() {
        return getInterface().isValid();
    }

    /**
     * Checks whether or not the deposit box is open.
     *
     * @return      <tt>true</tt> if the deposit box interface is open;
     *              otherwise <tt>false</tt>.
     */
    public boolean isDepositOpen() {
        return methods.ifaces.getInterface(INTERFACE_DEPOSIT_BOX).isValid();
    }

    private static class ReachableBankerFilter implements Filter<RSNPC> {

        @Override
        public boolean accept(final RSNPC npc) {
            final int id = npc.getID();
            final RSTile location = npc.getLocation();
            for (final int banker : Bankers) {
                if (banker == id) {
                    for (final RSTile unreachBanker : UnreachableBankers) {
                        if (unreachBanker.equals(location))
                            return false;
                    }
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * Opens one of the supported banker NPCs, booths, or chests nearby. If they
     * are not nearby, and they are not null, it will automatically walk to the
     * closest one.
     *
     * @return      <tt>true</tt> if the bank was opened; otherwise <tt>false</tt>.
     */
    public boolean open() {
        if (isOpen())
            return true;
        try {
            if (methods.isMenuOpen()) {
                methods.moveMouseSlightly();
                methods.wait(methods.random(20, 30));
            }
            RSObject bankBooth = methods.getNearestObjectByID(Bank.BankBooths);
            RSNPC banker = methods.getNPCByFilter(new ReachableBankerFilter());
            final RSObject bankChest = methods.getNearestObjectByID(Bank.BankChests);
            /* Find closese one,others are set to null. Remember distance and tile. */
            int lowestDist = Integer.MAX_VALUE;
            RSTile tile = null;
            if (bankBooth != null) {
                tile = bankBooth.getLocation();
                lowestDist = methods.distanceTo(tile);
            }
            if (banker != null && methods.distanceTo(banker) < lowestDist) {
                tile = banker.getLocation();
                lowestDist = methods.distanceTo(tile);
                bankBooth = null;
            }
            if (bankChest != null && methods.distanceTo(bankChest) < lowestDist) {
                tile = bankChest.getLocation();
                lowestDist = methods.distanceTo(tile);
                bankBooth = null;
                banker = null;
            }
            /* Open closest one, if any found */
            if (lowestDist < 5 && methods.tileOnMap(tile) && methods.canReach(tile, true)) {
                boolean didAction = false;
                if (bankBooth != null) {
                    didAction = bankBooth.action("Use-Quickly");
                } else if (banker != null) {
                    didAction = banker.action("Bank ");
                } else if (bankChest != null) {
                    didAction = bankChest.action("Bank") || methods.atMenu("Use");
                }
                if (didAction) {
                    int count = 0;
                    while (!isOpen() && ++count < 10) {
                        methods.wait(methods.random(200, 400));
                        if (methods.getMyPlayer().isMoving()) {
                            count = 0;
                        }
                    }
                } else {
                    methods.turnToTile(tile);
                }
            } else if (tile != null) {
                methods.walkTileMiniM(tile);
            }
            return isOpen();
        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Opens one of the supported deposit boxes nearby.
     * If they are not nearby, and they are not null,
     * it will automatically walk to the closest one.
     *
     * @return      <tt>true</tt> if the deposit box was opened; otherwise
     *              <tt>false</tt>.
     */
    public boolean openDepositBox() {
        try {
            if (!isDepositOpen()) {
                if (methods.isMenuOpen()) {
                    methods.moveMouseSlightly();
                    methods.wait(methods.random(20, 30));
                }
                final RSObject depositBox = methods.getNearestObjectByID(
                        BankDepositBox);
                if (depositBox != null && methods.distanceTo(depositBox)
                        < 8 && methods.tileOnMap(
                        depositBox.getLocation()) && methods.canReach(
                        depositBox.getLocation(), true)) {
                    if (depositBox.action("Deposit")) {
                        int count = 0;
                        while (!isDepositOpen() && ++count < 10) {
                            methods.wait(methods.random(200, 400));

                            if (methods.getMyPlayer().isMoving()) {
                                count = 0;
                            }
                        }
                    } else {
                        methods.turnToObject(depositBox, 20);
                    }
                } else {
                    if (depositBox != null) {
                        methods.walkTo(depositBox.getLocation());
                    }
                }
            }
            return isDepositOpen();
        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Opens the bank tab.
     *
     * @param tabNumber     The tab number - e.g. view all is 1.
     * @return              <tt>true</tt> on success.
     * @since               6 March 2009.
     */
    public boolean openTab(final int tabNumber) {
        return isOpen()
                && methods.atInterface(INTERFACE_BANK, INTERFACE_BANK_TAB[tabNumber - 1]);
    }

    /**
     * @return      <tt>true</tt> if currently searching the bank.
     */
    public boolean isSearchOpen() {
        // Setting 1248 is -2147483648 when search is enabled and -2013265920
        return methods.getSetting(1248) == -2147483648;
    }

    /**
     * Searches for an item in the bank. Returns true if succeeded (does not
     * necessarily mean it was found).
     * 
     * @param itemName      The item name to find.
     * @return              <tt>true</tt> on success.
     */
    public boolean searchItem(final String itemName) {
        if (!isOpen())
            return false;

        methods.atInterface(INTERFACE_BANK, INTERFACE_BANK_BUTTON_SEARCH, "Search");
        methods.wait(methods.random(1000, 2000));

        if (isOpen()
                && methods.ifaces.getInterface(INTERFACE_BANK_SEARCH).isValid()) {
            methods.getBot().getInputManager().sendKeys(itemName, false);
            methods.wait(methods.random(300, 700));
            return true;
        }
        return false;
    }

    /**
     * Sets the bank rearrange mode to insert.
     * 
     * @return      <tt>true</tt> on success.
     */
    public boolean setRearrangeModeToInsert() {
        if (!isOpen())
            return false;
        if (methods.getSetting(SETTING_BANK_TOGGLE_REARRANGE_MODE) != 1) {
            methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_INSERT);
            methods.wait(methods.random(500, 700));
        }
        return methods.getSetting(SETTING_BANK_TOGGLE_REARRANGE_MODE) == 1;
    }

    /**
     * Sets the bank rearrange mode to swap.
     * 
     * @return      <tt>true</tt> on success.
     */
    public boolean setRearrangeModeToSwap() {
        if (!isOpen())
            return false;
        if (methods.getSetting(SETTING_BANK_TOGGLE_REARRANGE_MODE) != 0) {
            methods.atInterface(INTERFACE_BANK,
                    INTERFACE_BANK_BUTTON_SWAP);
            methods.wait(methods.random(500, 700));
        }
        return methods.getSetting(SETTING_BANK_TOGGLE_REARRANGE_MODE) == 0;
    }

    /**
     * Sets the bank withdraw mode to item.
     * 
     * @return      <tt>true</tt> on success.
     */
    public boolean setWithdrawModeToItem() {
        if (!isOpen())
            return false;
        if (methods.getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) != 0) {
            methods.atInterface(INTERFACE_BANK, INTERFACE_BANK_BUTTON_ITEM);
            methods.wait(methods.random(500, 700));
        }
        return methods.getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) == 0;
    }

    /**
     * Sets the bank withdraw mode to note.
     * 
     * @return      <tt>true</tt> on success.
     */
    public boolean setWithdrawModeToNote() {
        if (!isOpen())
            return false;
        if (methods.getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) != 1) {
            methods.atInterface(INTERFACE_BANK, INTERFACE_BANK_BUTTON_NOTE);
            methods.wait(methods.random(500, 700));
        }
        return methods.getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) == 1;
    }

    /**
     * Tries to withdraw an item.
     * <p/>
     * 0 is All. 1,5,10 use Withdraw 1,5,10 while other numbers Withdraw X.
     * 
     * @param itemID        The ID of the item.
     * @param count         The number to withdraw.
     * @return              <tt>true</tt> on success.
     */
    public boolean withdraw(final int itemID, final int count) {
        if (!isOpen())
            return false;
        if (count < 0)
            throw new IllegalArgumentException("count < 0 (" + count + ")");
        final RSItem rsitem = getItem(itemID);
        if (rsitem == null)
            return false;
        final RSInterfaceComponent item = getItemByID(itemID);
        if ((item == null))
            return false;
        while (item.getRelativeX() == 0 && methods.bank.getCurrentTab() != 0) {
            if (methods.atInterface(INTERFACE_BANK, INTERFACE_BANK_TAB[0])) {
                methods.wait(methods.random(800, 1200));
            }
        }
        final int invCount = methods.getInventoryCount(true);
        item.click(count == 1 ? true : false);
        final String defaultAction = "Withdraw-" + count;
        String action = null;
        switch (count) {
            case 0:
                action = "Withdraw-All";
                break;
            case 1:
                break;
            case 5:
                action = defaultAction;
                break;
            case 10:
                action = defaultAction;
                break;
            default:
                int i = -1;
                try {
                    i = Integer.parseInt(item.getActions()[3].toLowerCase().
                            trim().replaceAll("\\D", ""));
                } catch (final Exception e) {
                    e.printStackTrace();
                }
                if (i == count) {
                    action = defaultAction;
                } else if (item.action("Withdraw-X")) {
                    methods.wait(methods.random(1200, 1500));
                    methods.getBot().getInputManager().sendKeys("" + count, true);
                }
        }
        if (action != null && item.action(action)) {
            methods.wait(methods.random(1000, 1300));
        }
        final int newInvCount = methods.getInventoryCount(true);
        return newInvCount > invCount || newInvCount == 28;
    }
}
